#pragma once
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include "msgpack/pack.hpp"
#include "msgpack/unpack.hpp"
#include "msgpack/type.hpp"
#include "buffer.hpp"
#include "head.h"
#include "crc32.h"

namespace KRPC {

template<typename R>
static inline bool unpack(const MsgHead *head, R &ret)
{
	if (head->len > sizeof(MsgHead)) {
		try {
			msgpack::unpack(head->data, head->len - sizeof(MsgHead)).get().convert(ret);
			return true;
		} catch (...) {}
	}
	return false;
}

class Packet
{
public:
	Packet() = default;
	Packet(std::size_t initsz, std::size_t wz) : m_buf(initsz, wz) {}
	Packet(KRPC::Buffer &&rcvbuf) : m_buf(std::move(rcvbuf)) {}

	KRPC::Buffer release() {return std::move(m_buf);}

	const void *data() const noexcept {return m_buf.data();}
	void *data() {return m_buf.data();}

	std::size_t size() const noexcept {return m_buf.len();}

	void resize(std::size_t sz) {m_buf.resize(sz);}

	void reset(std::size_t wz = 0) {m_buf.reset(wz);}

	void write(const char *buf, size_t len) {m_buf.write(buf, len);}

	MsgHead *getHead()
	{
		if (m_buf.size() < sizeof(MsgHead)) 
			m_buf.resize(1024);
		if (m_buf.len() < sizeof(MsgHead))
			m_buf.reset(sizeof(MsgHead));
		return (MsgHead *)(m_buf.data());
	}
	const MsgHead *getHead() const noexcept {return (const MsgHead *)(m_buf.data());}

	int getMode() const {return getHead()->mode;}
	void setMode(MsgMode mode) {getHead()->mode = mode;}

	template<typename T>
	void pack(T &&t)
	{
		if (m_buf.len() == sizeof(MsgHead)) {
			msgpack::pack(m_buf, t);
		} else if (m_buf.len() > sizeof(MsgHead)) {
			m_buf.reset(sizeof(MsgHead));
			msgpack::pack(m_buf, t);
		} else { 
			return;
		}

		MsgHead *head = (MsgHead *)(m_buf.data());
		head->len = m_buf.len();
		if (head->ver == 1)
			head->crc32 = CRC32(head->data, head->len - sizeof(MsgHead));
	}

	void pack() 
	{
		if (m_buf.len() > sizeof(MsgHead))
			m_buf.reset(sizeof(MsgHead));
		MsgHead *h = (MsgHead *)(m_buf.data());
		h->len = sizeof(MsgHead);
		h->crc32 = 0;
	}
	void pack(std::tuple<>) {pack();}

	template<typename R>
	bool unpack(R &ret) const //针对返回参数是非void
	{
		const MsgHead *head = (const MsgHead *)(m_buf.data());
		return KRPC::unpack(head, ret);
	}

	bool unpack(std::tuple<> &) const {return true;}
	bool unpack() const {return true;}

	bool valid() const
	{
		const MsgHead *head = (const MsgHead *)(m_buf.data());
		bool result = (m_buf.len() >= sizeof(MsgHead) && m_buf.size() >= m_buf.len() && head->len == m_buf.len());
		if (result && head->len > sizeof(MsgHead)) {
			switch (head->ver) {
				case 0: break;	//版本为0不做任何操作
				case 1: result = (CRC32(head->data, head->len - sizeof(MsgHead)) == head->crc32); break;
				default: result = false;	//其他版本暂不支持
			}
		}
		return result;
	}

private:
	KRPC::Buffer m_buf;
};

} //end of namespace KRPC
